package dev.ece.util.lib;

import org.lwjgl.opengl.GL20;

import dev.ece.util.Matrix;

/**
 * 3维向量
 * @author 潜行的青衣
 * @see
 */
public class Vector3f extends Vector2f {

	public Vector3f() {
		super(new float[3]);
	}
	
	public Vector3f(Vector3f vector) {
		super(new float[] {vector.getX(), vector.getY(), vector.getZ()});
	}
	
	public Vector3f(float x, float y, float z) {
		super(new float[] {x, y, z});
	}
	
	protected Vector3f(float[] vector) {
		super(vector);
	}

	public void set(float x, float y, float z) {
		this.setX(x);
		this.setY(y);
		this.setZ(z);
	}
	
	public void set(Vector3f vec) {
		this.set(vec.getX(), vec.getY(), vec.getZ());
	}
	
	public void splus(Vector3f start, Vector3f end) {
		this.set(end.getX() - start.getX(), end.getY() - start.getY(), end.getZ() - start.getZ());
	}
	
	public float length() {
		return Matrix.length(this.getX(), this.getY(), this.getZ());
	}
	
	public void add(float x, float y, float z) {
		this.setX(this.getX() + x);
		this.setY(this.getY() + y);
		this.setZ(this.getZ() + z);
	}
	
	public void setLength(float length) {
		if(length > 0) {
			this.normalize();
			this.set(this.getX() * length, this.getY() * length, this.getZ() * length);
		}
	}
	
	public void add(float length) {
		if(length > 0) {
			float l = this.length();
			float x = this.getX();
			float y = this.getY();
			float z = this.getZ();
			float rate = 1 + length / l;
			this.set(x * rate, y * rate, z * rate); 
		}
	}
	
	public void normalize() {
		float l = this.length();
		if(l == 0) {
			throw new IllegalStateException("向量长度为0！");
		}
		this.setX(this.getX() / l);
		this.setY(this.getY() / l);
		this.setZ(this.getZ() / l);
	}
	
	/**
	 * 插值运算
	 * (v2 - v1) * alpha + v1 = v1 * (1 - alpha) + v2 * alpha;
	 * @param v1
	 * @param v2
	 * @param alpha
	 */
	public final Vector3f interpolate(Vector3f v1, Vector3f v2, float alpha) {
		float leftAlpha = 1.0f - alpha;
		this.setX(leftAlpha * v1.getX() + alpha * v2.getX());
		this.setY(leftAlpha * v1.getY() + alpha * v2.getY());
		this.setZ(leftAlpha * v1.getZ() + alpha * v2.getZ());
		return this;
	}
	
	public javax.vecmath.Vector3f toVector3f() {
		return new javax.vecmath.Vector3f(this.getVector());
	}
	
	public void fromVector3f(javax.vecmath.Vector3f vector) {
		this.set(vector.x, vector.y, vector.z);
	}
	
	public static Vector3f from(javax.vecmath.Vector3f vector) {
		Vector3f result = new Vector3f();
		result.fromVector3f(vector);
		return result;
	}
	
	public Vector3f cross(Vector3f v) {
		return cross(this, v);
	}
	
	public float dot(Vector3f v) {
		return dot(this, v);
	}
	
	public void glUniform3f(int location) {
		GL20.glUniform3f(location, this.getX(), this.getY(), this.getZ());
	}
	
	public void setZ(float z) {
		this.getVector()[2] = z;
	}
	
	public float getZ() {
		return getVector()[2];
	}

	/**
	 * 向量交叉
	 * i = (1,0,0) j = (0,1,0) k = (0,0,1)。
	 * @param u 向量u
	 * @param v 向量v
	 * @return (Yu*Zv – Zu*Yv)*i + (Zu*Xv – Xu*Zv)j + (Xu*Yv – Yu*Xv)k;
	 */
	public static Vector3f cross(Vector3f u, Vector3f v) {
		Vector3f uv = new Vector3f();
		uv.setX(u.getY() * v.getZ() - u.getZ() * v.getY());
		uv.setY(u.getZ() * v.getX() - u.getX() * v.getZ());
		uv.setZ(u.getX() * v.getY() - u.getY() * v.getX());
		return uv;
	}
	
	public static float dot(Vector3f v1, Vector3f v2) {
		return v1.getX() * v2.getX() + v1.getY() * v2.getY() + v1.getZ() * v2.getZ();
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj == null) {
			return false;
		}
		if(!(obj instanceof Vector3f)) {
			return false;
		}
		Vector3f v = (Vector3f) obj;
		return v.getX() == this.getX() && v.getY() == this.getY() && v.getZ() == this.getZ();
	}
	
}
